/******************************************************************************
*
* Copyright (C), 2013, mophie. Co., Ltd. All Rights Reserved. 
*
* Version: v1.0.0
* $$Datae: 2013-7-5  
*
* Description:    
*   IIC Function
*
* Function List: 
* 
* History:        
*         1. $$data:  2013-7-5
*            Author:  JPM team         
*
******************************************************************************/
/***Include file list*********************************************************/
#include "twi_api.h"
#include "twi.h"
#include "io_api.h"

uint8_t return_value;

/**********function list******************************************************/

/**********funciton***********************************************************/

/*****************************************************************************
** Function name:
**      TWI_init

** Descriptions:
**      initialize the TWI interface  
**
** parameters:
**      p_twi------pointer for TOI 
**
** Returned value:
**      none
** 
*****************************************************************************/
void TWI_init(Twi *p_twi, uint32_t cpu_hz)
{
        uint32_t clk_div;
        if(p_twi == TWI0)
        {
                //config i/o pins for twi0
                TWI0_PORT->PIO_IDR |= (1U<<TWI0_TWD_PIN_NUM)|(1U<<TWI0_TWCK_PIN_NUM);
                TWI0_PORT->PIO_ABSR &= ~((1U<<TWI0_TWD_PIN_NUM)|(1U<<TWI0_TWCK_PIN_NUM));
                TWI0_PORT->PIO_PDR |= (1U<<TWI0_TWD_PIN_NUM)|(1U<<TWI0_TWCK_PIN_NUM);
                TWI0_PORT->PIO_PUDR |= (1U<<TWI0_TWD_PIN_NUM)|(1U<<TWI0_TWCK_PIN_NUM);
                
                //enable twi0 clock
                PMC->PMC_PCER0 |= 1U<<ID_TWI0;        
        }
        else if(p_twi == TWI1)
        {
                //config i/o pins for twi0
                TWI1_PORT->PIO_IDR |= (1U<<TWI1_TWD_PIN_NUM)|(1U<<TWI1_TWCK_PIN_NUM);
                TWI1_PORT->PIO_ABSR &= ~((1U<<TWI1_TWD_PIN_NUM)|(1U<<TWI1_TWCK_PIN_NUM));
                TWI1_PORT->PIO_PDR |= (1U<<TWI1_TWD_PIN_NUM)|(1U<<TWI1_TWCK_PIN_NUM);
                TWI1_PORT->PIO_PUDR |= (1U<<TWI1_TWD_PIN_NUM)|(1U<<TWI1_TWCK_PIN_NUM);
                
                //enable twi0 clock
                PMC->PMC_PCER0 |= 1U<<ID_TWI1;        
        }
        //disable all interrupt
        p_twi->TWI_IDR = 0xffffffff;
        //read state register to clear state bits
        p_twi->TWI_SR;
        //reset twi
	p_twi->TWI_CR |= TWI_CR_SWRST;
	p_twi->TWI_RHR;
        //enable twi master mode
	p_twi->TWI_CR |= TWI_CR_MSDIS|TWI_CR_SVDIS;
	p_twi->TWI_CR = TWI_CR_MSEN;
        //set speed to 400KHz
        clk_div = (cpu_hz/2/400000 - 4) & 0x000000ff;
        p_twi->TWI_CWGR = ((uint8_t)(clk_div))|(((uint8_t)(clk_div)) << 8)|(0<<16);
        //set speed to 50KHz
        //p_twi->TWI_CWGR = (209U<<0)|(209U<<8)|(2U<<16);
}

/*****************************************************************************
** Function name:
**      TWI_make_addr

** Descriptions:
**      make the internal address 
**
** parameters:
**      addr------pointer for internal addr 
**      len-------internal addr length
**
** Returned value:
**      val------the internal addr 
** 
*****************************************************************************/
static uint32_t TWI_make_addr(const uint8_t *addr, int len)
{
	uint32_t val;

	if (len == 0)
		return 0;

	val = addr[0];
	if (len > 1) {
		val <<= 8;
		val |= addr[1];
	}
	if (len > 2) {
		val <<= 8;
		val |= addr[2];
	}
	return val;
}

/*****************************************************************************
** Function name:
**      TWI_master_write

** Descriptions:
**      master write data to slave 
**
** parameters:
**      p_twi------pointer for internal addr 
**      twi_transfer_arg-------a struct for writing arg
**
** Returned value:
**      return TWI_SUCCESS if write successfully
**      return TWI_INVALID_ARGUMENT if the arg is invalid
**      return TWI_RECEIVE_NACK if ACK did't received
** 
*****************************************************************************/
uint32_t TWI_master_write(Twi *p_twi, twi_transfer_arg *p_transfer_arg)
{
	uint32_t status, cnt = p_transfer_arg->transfer_length;
	uint8_t *buffer = p_transfer_arg->buffer;

	/* Check argument */
	if (cnt == 0) {
		return(TWI_INVALID_ARGUMENT);
	}

	/* Set write mode, slave address and 3 internal address byte lengths */
	p_twi->TWI_MMR = 0;
	p_twi->TWI_MMR = (p_transfer_arg->deveic_addr << 16)|
			 (p_transfer_arg->inter_addr_length<<8);

	/* Set internal address for remote chip */
	p_twi->TWI_IADR = 0;
	p_twi->TWI_IADR = TWI_make_addr(p_transfer_arg->inter_addr,
                                        p_transfer_arg->inter_addr_length);

	/* Send all bytes */
	while (cnt > 0) {
		status = p_twi->TWI_SR;
		if (status & TWI_SR_NACK) {
			return TWI_RECEIVE_NACK;
		}

		if (!(status & TWI_SR_TXRDY)) {
			continue;
		}
		p_twi->TWI_THR = *buffer++;

		cnt--;
	};

	p_twi->TWI_CR = TWI_CR_STOP;

	while (!(p_twi->TWI_SR & TWI_SR_TXCOMP)) {
	}

	p_twi->TWI_SR;

	return TWI_SUCCESS;
}

/*****************************************************************************
** Function name:
**      TWI_master_read

** Descriptions:
**      master read data from slave 
**
** parameters:
**      p_twi------pointer for internal addr 
**      twi_transfer_arg-------a struct for writing arg
**
** Returned value:
**      return TWI_SUCCESS if write successfully
**      return TWI_INVALID_ARGUMENT if the arg is invalid
**      return TWI_RECEIVE_NACK if ACK did't received
** 
*****************************************************************************/
uint32_t TWI_master_read(Twi *p_twi, twi_transfer_arg *p_transfer_arg)
{
	uint32_t status, cnt = p_transfer_arg->transfer_length;
	uint8_t *buffer = p_transfer_arg->buffer;
	
	/* Check argument */
	if (cnt == 0) {
		return TWI_INVALID_ARGUMENT;
	}

	/* Set read mode, slave address and 3 internal address byte lengths */
	p_twi->TWI_MMR = 0;
	p_twi->TWI_MMR = TWI_MMR_MREAD | (p_transfer_arg->deveic_addr<<16) |
			 (p_transfer_arg->inter_addr_length << 8);

	/* Set internal address for remote chip */
	p_twi->TWI_IADR = 0;
	p_twi->TWI_IADR = TWI_make_addr(p_transfer_arg->inter_addr,
                                        p_transfer_arg->inter_addr_length);

	/* Send a START Condition */
	p_twi->TWI_CR = TWI_CR_START;

	while (cnt > 0) {
		status = p_twi->TWI_SR;
		if (status & TWI_SR_NACK) {
			return TWI_RECEIVE_NACK;
		}

		/* Last byte ? */
		if (cnt == 1) {
			p_twi->TWI_CR = TWI_CR_STOP;
		}

		if (!(status & TWI_SR_RXRDY)) {
			continue;
		}
		*buffer++ = p_twi->TWI_RHR;

		cnt--;
	}

	while (!(p_twi->TWI_SR & TWI_SR_TXCOMP)) {
	}

	p_twi->TWI_SR;

	return TWI_SUCCESS;
}

/*****************************************************************************/

